package com.twitter.home_mixer.product.scored_tweets.scorer

import com.twitter.home_mixer.model.HomeFeatures.ScoreFeature
import com.twitter.home_mixer.product.scored_tweets.model.ScoredTweetsQuery
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
import com.twitter.product_mixer.core.feature.Feature
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
import com.twitter.product_mixer.core.functional_component.scorer.Scorer
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
import com.twitter.product_mixer.core.model.common.identifier.ScorerIdentifier
import com.twitter.stitch.Stitch

/**
 * Apply various heuristics to the model score
 */
//是的，HeuristicScorer 类在 Twitter 的推文评分系统中类似于常见搜索推广中的重排环节。
// 这个环节的主要任务是对模型得出的初始评分进行再加工和调整，以优化最终展示给用户的推文质量。下面是对这个环节做的主要事情的详细介绍：
//主要作用
//优化推文展示：通过对原始评分进行细微调整，可以更好地符合用户的喜好和行为，提高推文的相关性和吸引力。
//平衡多种因素：考虑到除了模型评分之外的其他因素，如作者多样性、用户反馈疲劳等，使推文展示更加全面和平衡。
//增强用户体验：最终目的是为用户提供更优质、更相关的内容，提高用户满意度和参与度。
object HeuristicScorer extends Scorer[ScoredTweetsQuery, TweetCandidate] {

  override val identifier: ScorerIdentifier = ScorerIdentifier("Heuristic")

  override val features: Set[Feature[_, _]] = Set(ScoreFeature)

  override def apply(
    query: ScoredTweetsQuery,
    candidates: Seq[CandidateWithFeatures[TweetCandidate]]
  ): Stitch[Seq[FeatureMap]] = {
    //这里定义了一系列的重排规则（rescorers），如 RescoreOutOfNetwork、RescoreReplies 等。
    // 每个规则代表一种特定的启发式逻辑，例如对来自网络外的推文打分、对回复进行重新评分等。
    val rescorers = Seq(
      RescoreOutOfNetwork,
      RescoreReplies,
      RescoreBlueVerified,
      RescoreCreators,
      RescoreMTLNormalization,
      RescoreAuthorDiversity(AuthorDiversityDiscountProvider(candidates)),
      RescoreFeedbackFatigue(query)
    )
    //对于每个推文候选项，先获取其原始评分，然后应用所有启发式规则来计算一个缩放因子（scaleFactor）。
    // 这个缩放因子是所有重排规则的结果的乘积，用来调整原始评分。
    val updatedScores = candidates.map { candidate =>
      val score = candidate.features.getOrElse(ScoreFeature, None)
      val scaleFactor = rescorers.map(_(query, candidate)).product
      val updatedScore = score.map(_ * scaleFactor)
      FeatureMapBuilder().add(ScoreFeature, updatedScore).build()
    }

    Stitch.value(updatedScores)
  }
}
